package edu.kit.csl.pisa.io;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.kit.csl.pisa.datatypes.Corpus;
import edu.kit.csl.pisa.datatypes.Dictionary;
import edu.kit.csl.pisa.datatypes.Sentence;
import edu.kit.csl.pisa.datatypes.SentencePair;

/**
 * This class uses the GoF design pattern Abstract Factory. For each different
 * possible value of the parameter initialModelLoader there is a subclass of this
 * class, which is then able to load the initial corpus from initialModelPath.
 */
public abstract class CorpusFactory {
	
	private final String SRC_SEN_PATTERN = " \\([{} l0-9]+\\)";
	
	private final String SENTENCE_ID_PATTERN = "Sentence pair \\(([^\\)]+)\\)";
	
	private final String ALIGN_PATTERN = "\\(\\{([ l0-9]+)\\}\\)";
	
	protected final Pattern senIdPattern, alignPattern;
	
	/**
	 * @see #updateAlignmentArrays()
	 */
	protected short[] alignment = null; // like in (Brown et al,94)
	protected boolean[] locks = null;
	
	/**
	 * Sole constructor. Compiles the internal protected {@link Pattern}s
	 * senIdPattern and alignPattern.
	 */
	public CorpusFactory() {
		senIdPattern = Pattern.compile(SENTENCE_ID_PATTERN);
		alignPattern = Pattern.compile(ALIGN_PATTERN);
	}
	
	/**
	 * This is a factory method (GoF design pattern) which produces corpus
	 * instances. The strategies of loading it differ among the implementing
	 * subclasses, but the location of the resources have to be stored in the 
	 * parameter initialModelPath.
	 * 
	 * @return a corpus loaded from initialModelPath
	 */
	public abstract Corpus createCorpus();
	
	
	/**
	 * Creates a sentence pair instance from string representations of the 
	 * target sentence (plain) and the source sentence (formatted as GIZA++
	 * alignment string).
	 * 
	 * @param trgtString target sentence string
	 * @param alignString alignment string (containing the source sentence)
	 * @param id sentence pair id
	 * @return a new {@link SentencePair} instance
	 */
	protected SentencePair createSentencePair(String trgtString, String alignString,
			String id) {
		// Fetch target sentence (phoneme string)
		Sentence srcSen = new Sentence(
				alignString.replaceAll(SRC_SEN_PATTERN, ""),
				Dictionary.getSingleton(Dictionary.SRC_DICT));
		Sentence trgtSen = new Sentence(
				trgtString,
				Dictionary.getSingleton(Dictionary.TRGT_DICT));
		return new SentencePair(srcSen, trgtSen, id);
	}
	
	/**
	 * Updates the arrays alignment and locks according the given alignment
	 * string.
	 * 
	 * @param alignString
	 * @param trgtLen length of the target sentence
	 * @throws ArrayIndexOutOfBoundsException if alignString is malformed
	 */
	protected void updateAlignmentArrays(String alignString, int trgtLen) {
		alignment = new short[trgtLen]; // like in (Brown et al,94)
		locks = new boolean[trgtLen];
		Arrays.fill(locks, false);
		// Create alignment array
		Matcher matcher = alignPattern.matcher(alignString);
		for (short srcPos = 0; matcher.find(); srcPos++) {
			for (String trgtPos : matcher.group(1).split(" ")) {
				if (!trgtPos.isEmpty()) {
					int lastIdx = trgtPos.length() - 1;
					int trgtIdx = 0;
					if (trgtPos.charAt(lastIdx) == 'l') { // Locked
						trgtIdx = Integer.parseInt(
								trgtPos.substring(0, lastIdx)) - 1;
						locks[trgtIdx] = true;
					} else {
						trgtIdx = Integer.parseInt(trgtPos) - 1;
					}
					alignment[trgtIdx] = srcPos;
				}
			}
		}
	}
}
